﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Badger.Vhdl.Types;
using Weazel.Badger.Vhdl.Expressions;

#endregion

namespace Weazel.Badger.Vhdl.Statements
{
  public class IfStatement : SequentialStatement, IStatementCollection, IComponent
  {
    private Expression condition;
    public Expression Condition
    {
      get
      {
        return condition;
      }

      set
      {
        condition = value;
      }
    }

    private IfStatement elsIfStatement;
    public IfStatement ElsIfStatement
    {
      get
      {
        return elsIfStatement;
      }
      
      set
      {        
        elsIfStatement = value;
      }
    }


    private StatementCollection trueStatements;
    public StatementCollection TrueStatements
    {
      get
      {
        return trueStatements;
      }      
      set
      {       
        trueStatements = value;
      }      
    }

    /// <summary>
    /// statements to execute if the condition evaluates
    /// to false. Note: will only be inluded if the 
    /// ElsIfStatement is null.
    /// </summary>
    private StatementCollection falseStatements;
    public StatementCollection FalseStatements
    {
      get
      {
        return falseStatements;
      }      
      set
      {
        falseStatements = value;
      }      
    }

    public IfStatement()
    {
      falseStatements = new StatementCollection();
      trueStatements = new StatementCollection();
    }

		public override void Resolve(Entity entity, Process process, StatementCollection statements)
    {
      if (condition == null)
      {
        throw new NullReferenceException("Condition not set for if-statement");
      }

      TypeSet possibleTypes = condition.GetPossibleTypes();

      if(possibleTypes.Contains(Types.Types.Boolean))      
      {
				condition.GetPossibleTypes();
				condition.SetActualType(entity, process, statements, Types.Types.CreateType(Types.Types.Boolean, 1, false));
      }
			else if (possibleTypes.Contains(Types.Types.StdLogicVector))
			{
				// convert to a boolean expression by comparing with '1'
				Vhdl.Expressions.BinaryExpression equalsExpr =
					new Vhdl.Expressions.BinaryExpression(
						condition.ParentContainer,
						Vhdl.Operators.BinaryOperators.GreaterThan,
						condition,
						new Vhdl.Expressions.ConstantExpression(
							condition.ParentContainer,
							"0",
							new Vhdl.Types.Integer(2),
							null
						),
						new Types.Boolean(),
						null
					);

				condition = equalsExpr;

				// GetPossibleTypes MUST be called.
				System.Diagnostics.Debug.Assert(condition.GetPossibleTypes().Contains(Types.Types.Boolean));

				condition.GetPossibleTypes();
				condition.SetActualType(entity, process, statements, Types.Types.CreateType(Types.Types.Boolean, 1, false));
			}
			else if (possibleTypes.Contains(Types.Types.StdLogic))
			{
				// convert to a boolean expression by comparing with '1'

				Vhdl.Expressions.BinaryExpression equalsExpr =
					new Vhdl.Expressions.BinaryExpression(
						condition.ParentContainer,
						Vhdl.Operators.BinaryOperators.Equals,
						condition,
						new Vhdl.Expressions.ConstantExpression(
							condition.ParentContainer,
							"'1'",
							new StdLogic(),
							null
						),
						new Types.Boolean(),
						null
					);

				condition = equalsExpr;

				// GetPossibleTypes MUST be called.
				System.Diagnostics.Debug.Assert(condition.GetPossibleTypes().Contains(Types.Types.Boolean));

				condition.GetPossibleTypes();
				condition.SetActualType(entity, process, statements, Types.Types.CreateType(Types.Types.Boolean, 1, false));
			}
			else
			{
				throw new NotSupportedException("If-statement condition is not boolean or std_logic");
			}

      // resolve true and false statements
			trueStatements.Resolve(entity, process, trueStatements);
			falseStatements.Resolve(entity, process, falseStatements);

      // resolve any following 'elsif' statements
      if (elsIfStatement != null)
				elsIfStatement.Resolve(entity, process, statements);
    }

    public override void Verify(ref List<VerifyMessage> messages)
    {
      trueStatements.Verify(ref messages);
      falseStatements.Verify(ref messages);

      if (elsIfStatement != null)
        elsIfStatement.Verify(ref messages);
    }

    public override void Write(System.CodeDom.Compiler.IndentedTextWriter writer)
    {
      Write(writer, true);
    }

    public void Write(System.CodeDom.Compiler.IndentedTextWriter writer, bool terminate)
    {
      writer.WriteLine("if ({0}) then", condition.ToString());
      writer.Indent++;

      trueStatements.Write(writer);

      writer.Indent--;

      if (elsIfStatement != null)
      {
        writer.Write("els"); // the following 'if' will be written by 'elsIfStatement'
        elsIfStatement.Write(writer, false);
      }

      if (falseStatements.Count > 0)
      {
        writer.WriteLine("else");
        writer.Indent++;

        falseStatements.Write(writer);

        writer.Indent--;
      }

      if(terminate)
        writer.WriteLine("end if;");
    }
  }
}
